home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Magazin/MacEasy 19
/
Mac Magazin and MacEasy Magazine CD - Issue 19.iso
/
Musik & Kunst
/
Ear Workout 2.1
/
source code
/
ear_chord.cp
< prev
next >
Wrap
Text File
|
1996-01-15
|
36KB
|
1,030 lines
//
//
// shortcomings:
// -- doesn't play enough 7th chords with missing 3, 5
// -- since the synthesizer I'm using can only play 4 notes, we're limited to 4-note
// chords
// -- Doesn't know much about how to choose among different interpretations of
// the same chord. All it knows is that if it has a m7 chord with the
// 3 in the bass, it should reinterpret it as a 6 chord.
// -- Should give hints about how you could have recognized the previous chord,
// e.g., it contains an aug triad,...
// -- when the difficulty level is very high, it still ends up playing quite
// a few ordinary chords, because it thinks them up in some convoluted
// way (e.g. m add b6) but then simplify_chord realizes they're
// more ordinary (maj 7). On high difficulty levels, should usually
// reject these chords that turn out to be ordinary, and try again.
// -- On high difficulty levels, there is a tendency to make chords that are
// too easy by use of the omitted notes. Wrote special cases to
// reject some of these, e.g. 7th chords with omitted roots
// are really triads.
// -- choose_chord() operates by choosing a random set of allowed items that is
// not too dissonant, then simplifying the list of items,
// then finally making sure all the items on the new, simpler, list
// are allowed. Unfortunately, this could result in the program never
// playing a chord that the user wants it to play.
// Alternatives: (1) Could revert to unsimplified form if the
// simplified form is forbidden, but then they might never learn that,
// e.g., a dim add b7 is the same as a half-dim. (2) When they click
// on a check box to enable or disable an item, could try to make
// the other check boxes consistent. E.g. if they have half-dim
// disabled, dim enabled, and then they enable the b7 added note,
// we could enabled half-dim as a side-effect. This would confuse them,
// though, and they wouldn't be able to go down the list of check boxes
// and click on them all in a row.
// Compromise: If simplification results in a
// reduction of number of items by one or less, and the simplified
// version uses disabled items, go back to the more complicated
// version.
// -- Chords may sound to a musician more like
// some other conventional chord but with omitted notes, e.g.
// B dim add 11 would probably be recognized as G 13 (no 1, no 9
// no 11).
// -- Clicking on buttons to get rid of choices sometimes causes a chord
// symbol to be displayed that's not what they intended. If they get
// confused, though, they can always wipe out their guess and start over.
//
//
// 29 Nov 95 - Changed fiddle_with_chord_voicing so it includes intervals of 2 and b2
// more often.
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <OSUtils.h>
#include <QuickDraw.h>
#include <Sound.h>
#define NEED_MAC_STUFF 1
#include "Ninkasi:C++ util:generic.h"
#include "Ninkasi:C++ util:complete_window.h"
#include "ear_defines.h"
#include "ear_decl.h"
#include "ear_prototypes.h"
DEN_MOTHER_T chord_den_mother;
void make_about_chord_window();
void choose_chord(int *item,int *n,double difficulty,int *enabled_list,int n_enabled,
int min_notes,int max_notes);
void fiddle_with_chord_voicing(int *chord,int n,int inversions_allowed);
int part_num_to_chord_item(int part,char *decoder_string);
unsigned int plain_chord_item(unsigned int);
double scale_difficulty(int raw);
//-- had strange problems with sign-extension on & of 4-byte ints!? compiler bug!?
#define IS_CHECK_BOX(chord_item_part) ((((unsigned) (chord_item_part)) & (unsigned) 0x8000L)!=0)
#define PLAIN_CHORD_ITEM(chord_item_part) plain_chord_item((unsigned) (chord_item_part))
#define MAXPART 200
// highest part number in our window
extern double fabs();
DEN_MOTHER_T about_chord_den_mother;
void make_window(int dlog_resource_id,
Str255 param_text0,
Str255 param_text1,
Str255 param_text2,
Str255 param_text3,
DEN_MOTHER_T *den_mother);
void
chord_den_mother(complete_window *my_complete_window)
{
//--- decoder string for controls:
static char *decoder_ptr =
"\0ok,play,majt,mint,dimt,augt,sust,b5t,7,m7,maj7,o7,h,b9,9,#9,11,#11,b6,6,a7,amaj7,\
no3,cmajt,cmint,cdimt,caugt,csust,cb5t,c7,cm7,cmaj7,co7,ch,cb9,c9,c#9,c11,c#11,\
cb6,c6,ca7,camaj7,cno3,t1,t2,t3,ic,sc,cl,cho,ar,play_arpeggio,bass,d,lower_difficulty,\
higher_difficulty,guess,help,no1,no5,\
cno1,cno5,inversions,wipe,correct_answer,t4,t5,play_previous";
//--- has to begin with null so subroutines know it's not really
// a Pascal string
static char **decoder_string = &decoder_ptr;
//--- their score:
static int ntries;
static int nright;
//--- keeping track of my current state:
static int first_time = 1;
static int they_have_chord_to_consider;
static int current_icon;
static int ready_to_update,need_to_redraw,need_to_redraw_all,pressed_ok,
play_it,made_about_chord_window;
//--- parameters that determine what chords I play and how I play them:
#define TYPICAL_TEMPO_CHORD 45
#define TYPICAL_TEMPO_ARPEGGIO 200
static double typical_tempo;
static int mean_note;
static double raw_difficulty;
static double difficulty;
static int enabled_list[MAXPART];
static int n_enabled;
static int chord_or_arpeggio;
static int min_notes;
static int max_notes;
static int inversions_allowed;
//--- about the current chord:
static int n_items,item[MAX_CHORD_ITEMS];
static int n_notes,chord[MAX_CHORD_ITEMS];
static int duration;
static int arpeggio_direction;
static int root;
//--- about the previous chord:
// These are globals because the about_chord den
// mother needs to know about them.
//
// ...
//
//--- their guess:
static int guess[MAX_CHORD_ITEMS];
static int n_guess = 0;
static int guessed_basic_part = 0;
char nifty_name[100];
int nifty_index,center,avg,i,right,they_gave_answer,cleared_scores,
chord_item_pressed,twiddle_check_box,twiddle_radio_buttons,
play_arpeggio,play_bass,changed_difficulty,changed_guess,
chose_new_chord,redraw_inversion_check_box,need_to_redraw_erased_buttons,
play_previous,activate_and_deactivate;
GrafPtr save_graf;
need_to_redraw = 0;
need_to_redraw_all = 0;
ready_to_update = 0;
pressed_ok = 0;
play_it = 0;
they_gave_answer = 0;
cleared_scores = 0;
twiddle_check_box = 0;
twiddle_radio_buttons = 0;
play_arpeggio = 0;
play_bass = 0;
changed_difficulty = 0;
changed_guess = 0;
chose_new_chord = 0;
redraw_inversion_check_box = 0;
need_to_redraw_erased_buttons = 0;
activate_and_deactivate = 0;
play_previous = 0;
if (first_time) { //-- this happens the first time our window is created,
// and also when it's been closed and recreated
int i,ii,ee,this_chord_item;
first_time = 0;
ntries = 0;
nright = 0;
current_icon = BLANK_ICON_ID;
chord_or_arpeggio = 1;
typical_tempo = TYPICAL_TEMPO_CHORD;
mean_note = 61;
raw_difficulty = 5;
difficulty = scale_difficulty(raw_difficulty);
min_notes = 3;
max_notes = 4;
inversions_allowed = 0;
they_have_chord_to_consider = 0;
have_previous_chord = 0;
made_about_chord_window = 0;
n_enabled = 0;
for (i=0; i<=MAXPART; i++) { //-- normally we break out of inside of loop
part_number_to_nifty_label(nifty_name,&nifty_index,*decoder_string,i);
if (nifty_index < 0) break;
this_chord_item = part_num_to_chord_item(i,*decoder_string);
if (this_chord_item!=0 && IS_CHECK_BOX(this_chord_item)) {
ii = PLAIN_CHORD_ITEM(this_chord_item);
if (ii<=HI_7TH_CHORD
&& ii!=CHORD_AUG_TRIAD
&& ii!=CHORD_SUS_TRIAD
&& ii!=CHORD_FLAT5_TRIAD
) {
add_to_list(enabled_list,&n_enabled,ii);
}
}
}
}
switch(my_complete_window->whassup) {
case complete_window_created:
need_to_redraw = 1;
need_to_redraw_all = 1;
break;
case complete_window_redraw:
need_to_redraw = 1;
need_to_redraw_all = 1;
ready_to_update = 1; //-- main program does begin update & sets graf port
break;
case complete_window_action:
part_number_to_nifty_label(nifty_name,&nifty_index,
*decoder_string,my_complete_window->part_number);
chord_item_pressed = part_num_to_chord_item(
my_complete_window->part_number,*decoder_string);
if (nifty_index != -999) {
if (strcmp(nifty_name,"ok")==0) {
pressed_ok = 1;
}
if (strcmp(nifty_name,"play")==0) {
play_it = 1;
}
if (strcmp(nifty_name,"play_previous")==0) {
play_previous = 1;
}
if (strcmp(nifty_name,"wipe")==0) {
changed_guess = 1;
need_to_redraw = 1;
guessed_basic_part = 0;
activate_and_deactivate = 1;
n_guess = 0;
}
if (strcmp(nifty_name,"help")==0) {
make_chord_help_window();
}
if (strcmp(nifty_name,"play_arpeggio")==0) {
play_arpeggio = 1;
play_it = 1;
}
if (strcmp(nifty_name,"bass")==0) {
play_bass = 1;
play_it = 1;
}
if (strcmp(nifty_name,"cl")==0) {
ntries = 0;
nright = 0;
cleared_scores = 1;
need_to_redraw = 1;
current_icon = BLANK_ICON_ID;
}
if (strcmp(nifty_name,"higher_difficulty")==0) {
raw_difficulty += 1;
if (raw_difficulty>20) raw_difficulty=20;
difficulty = scale_difficulty(raw_difficulty);
changed_difficulty = 1;
need_to_redraw = 1;
}
if (strcmp(nifty_name,"lower_difficulty")==0) {
raw_difficulty -= 1;
if (raw_difficulty<1) raw_difficulty=1;
difficulty = scale_difficulty(raw_difficulty);
changed_difficulty = 1;
need_to_redraw = 1;
}
if (strcmp(nifty_name,"inversions")==0) {
need_to_redraw = 1;
redraw_inversion_check_box = 1;
inversions_allowed = !inversions_allowed;
if (!inversions_allowed
&& is_in_list(enabled_list,n_enabled,CHORD_NO1)) {
remove_from_list(enabled_list,&n_enabled,CHORD_NO1);
twiddle_check_box = 1;
}
}
if (strcmp(nifty_name,"cho")==0) {
if (chord_or_arpeggio==2)
duration = duration * ((double) TYPICAL_TEMPO_ARPEGGIO)
/((double) TYPICAL_TEMPO_CHORD);
chord_or_arpeggio = 1;
need_to_redraw = 1;
twiddle_radio_buttons = 1;
typical_tempo = TYPICAL_TEMPO_CHORD;
}
if (strcmp(nifty_name,"ar")==0) {
if (chord_or_arpeggio==1)
duration = duration * ((double) TYPICAL_TEMPO_CHORD)
/((double) TYPICAL_TEMPO_ARPEGGIO);
chord_or_arpeggio = 2;
need_to_redraw = 1;
twiddle_radio_buttons = 1;
typical_tempo = TYPICAL_TEMPO_ARPEGGIO;
}
if (chord_item_pressed != 0) {
if (IS_CHECK_BOX(chord_item_pressed)) { //-- check box
unsigned int blargh,wugga;
twiddle_check_box = 1;
need_to_redraw = 1;
wugga = chord_item_pressed;
blargh = PLAIN_CHORD_ITEM(wugga);
if (is_in_list(enabled_list,n_enabled,blargh))
remove_from_list(enabled_list,&n_enabled,blargh);
else
add_to_list(enabled_list,&n_enabled,blargh);
}
else { //-- button
//-- the only thing to watch out for is if they
// change their mind about the basic part of
// the chord
if (chord_item_pressed<=HI_7TH_CHORD
&& guessed_basic_part) { //--replace basic part
for (i=0; i<n_guess; i++) {
if (guess[i]<=HI_7TH_CHORD)
remove_from_list(guess,&n_guess,guess[i]);
}
}
if (!is_in_list(guess,n_guess,chord_item_pressed)
|| chord_item_pressed<=HI_7TH_CHORD) {
add_to_list(guess,&n_guess,chord_item_pressed);
if (chord_item_pressed<=HI_7TH_CHORD) {
guessed_basic_part = 1;
activate_and_deactivate = 1;
}
}
else {
remove_from_list(guess,&n_guess,chord_item_pressed);
}
changed_guess = 1;
need_to_redraw = 1;
}
}
}//--end if they hit a valid control
break;
case complete_window_erase:
they_have_chord_to_consider = 0;
first_time = 1;
chord_window_exists = 0;
if (about_chord_window_exists && VALID_POINTER(where_is_about_chord_complete_window))
delete where_is_about_chord_complete_window;
return;
}
if (pressed_ok && guessed_basic_part) {
int actual_chord[MAX_CHORD_NOTES],guess_chord[MAX_CHORD_NOTES];
int n_actual_notes,n_guess_notes;
activate_and_deactivate = 1; //-- now that they've pressed ok, dim it
guessed_basic_part = 0; // ...
make_chord(actual_chord, &n_actual_notes, item, n_items);
make_chord(guess_chord, &n_guess_notes, guess, n_guess);
right = same_canonical_form(actual_chord,n_actual_notes,
guess_chord,n_guess_notes);
//-- do they have the same canonical form?
++ntries;
nright += right;
if (right) {
current_icon = CHECK_ICON_ID;
}
else {
current_icon = X_ICON_ID;
}
they_have_chord_to_consider = 0;
they_gave_answer = 1;
need_to_redraw = 1;
if (!have_previous_chord && !made_about_chord_window) {
made_about_chord_window = 1;
make_about_chord_window();
SelectWindow(my_complete_window->the_window);
// bring attention back to me!
}
have_previous_chord = 1;
previous_root = root;
previous_n_notes = n_notes;
copy_chord(previous_chord,chord,n_notes);
if (right) {
//-- If they gave the right answer, that is what we
// want to put down as the description of the previous
// chord. I.e., if they gave a valid alternative
// description of the chord, we don't want them
// to think they got it wrong.
int try_root,new_items[MAX_CHORD_ITEMS],new_n_items;
previous_n_items = n_guess;
copy_chord(previous_item,guess,n_guess);
//-- what root is implied by their guess?
for (try_root=0; try_root<=11; try_root++) {
analyze_chord_based_on_this_root(
new_items,&new_n_items,chord,n_notes,try_root);
if (new_n_items!=0 &&
lists_are_same_except_for_order(
new_n_items,new_items,n_guess,guess)) {
previous_root = try_root;
break;
}
}
}
else {
previous_n_items = n_items;
copy_chord(previous_item,item,n_items);
}
//-- let about_chord_den_mother know it should redraw itself
if (about_chord_window_exists) {
GrafPtr save_graf;
WindowPtr window;
window = where_is_about_chord_complete_window->the_window;
SelectWindow(window);
GetPort(&save_graf);
SetPort(window);
EraseRect(&window->portRect);
DrawControls(window);
where_is_about_chord_complete_window->whassup
= complete_window_redraw;
(*(where_is_about_chord_complete_window->den_mother))
(where_is_about_chord_complete_window);
SetPort(save_graf);
SelectWindow(my_complete_window->the_window);
// bring attention back to me!
}
}
if (need_to_redraw) {
if (!ready_to_update) {
GetPort(&save_graf);
SetPort(my_complete_window->the_window);
}
if (activate_and_deactivate || need_to_redraw_all) {
if (guessed_basic_part)
activate_ctl_by_nifty_label(my_complete_window->the_window,
"ok",1,*decoder_string);
else
deactivate_ctl_by_nifty_label(my_complete_window->the_window,
"ok",1,*decoder_string);
if (have_previous_chord)
activate_ctl_by_nifty_label(my_complete_window->the_window,
"play_previous",1,*decoder_string);
else
deactivate_ctl_by_nifty_label(my_complete_window->the_window,
"play_previous",1,*decoder_string);
}
if (twiddle_radio_buttons || need_to_redraw_all) {
set_ctl_by_nifty_label(my_complete_window->the_window,
"cho",1,*decoder_string,(chord_or_arpeggio==1));
set_ctl_by_nifty_label(my_complete_window->the_window,
"ar",1,*decoder_string,(chord_or_arpeggio==2));
}
if (twiddle_check_box || need_to_redraw_all) {
int yowza,this_chord_item,nifty_index;
char nifty_name[30];
for (yowza=0; yowza<=MAXPART; yowza++) { //-- normally we break out of inside of loop
part_number_to_nifty_label(nifty_name,&nifty_index,*decoder_string,yowza);
if (nifty_index < 0) break;
this_chord_item = part_num_to_chord_item(yowza,*decoder_string);
if (this_chord_item!=0 && IS_CHECK_BOX(this_chord_item)) {
set_ctl_by_nifty_label(my_complete_window->the_window,
nifty_name,nifty_index,*decoder_string,
is_in_list(enabled_list,n_enabled,
PLAIN_CHORD_ITEM(this_chord_item)));
}
}
}
if (need_to_redraw_all || redraw_inversion_check_box) {
set_ctl_by_nifty_label(my_complete_window->the_window,
"inversions",1,*decoder_string,inversions_allowed);
}
if (have_previous_chord
&& (need_to_redraw_all || they_gave_answer)) {
char s[200];
Handle h;
if (1) { //-- new method of showing right answer: guitar chord symbol
get_item_by_nifty_label(my_complete_window->the_window,
"correct_answer",1,*decoder_string,&h);
if (VALID_HANDLE(h)) {
describe_chord(s+1,previous_n_items,previous_item);
s[0] = strlen(s+1);
TextSize((short) 18);
TextFont(geneva);
SetIText(h,(unsigned char *) s);
normal_text_style();
}
}
}
if (they_gave_answer && !right) {
if (0) { //-- old method of showing right answer: erase buttons
int i,this_chord_item,nifty_index;
char nifty_name[30];
Handle h;
need_to_redraw_erased_buttons = 1;
for (i=0; i<=MAXPART; i++) { //-- normally we break out of inside of loop
part_number_to_nifty_label(nifty_name,&nifty_index,*decoder_string,i);
if (nifty_index < 0) break;
this_chord_item = part_num_to_chord_item(i,*decoder_string);
if (this_chord_item!=0 && !IS_CHECK_BOX(this_chord_item)
&& !is_in_list(item,n_items,this_chord_item)) {
hide_ctl_by_nifty_label(my_complete_window->the_window,
nifty_name,nifty_index,*decoder_string);
}
}
}
}
if (need_to_redraw_all) {
normal_text_style();
refresh_text(my_complete_window,"t1",1,*decoder_string);
refresh_text(my_complete_window,"t2",1,*decoder_string);
refresh_text(my_complete_window,"t3",1,*decoder_string);
refresh_text(my_complete_window,"t4",1,*decoder_string);
refresh_text(my_complete_window,"t5",1,*decoder_string);
normal_text_style();
}
if (need_to_redraw_all || changed_difficulty) {
{
Handle h;
char s[50];
Rect r;
short x,y;
PolyHandle arrowhead;
get_item_by_nifty_label(my_complete_window->the_window,
"d",1,*decoder_string,&h);
if (VALID_HANDLE(h)) {
sprintf(s+1,"difficulty: %d",(int) raw_difficulty);
s[0] = strlen(s+1);
SetIText(h,(unsigned char *) s);
refresh_text(my_complete_window,"d",1,*decoder_string);
}
}
}
if (they_gave_answer || cleared_scores || need_to_redraw_all) {
Handle h;
char s[50];
int i,tot;
get_item_by_nifty_label(my_complete_window->the_window,
"sc",1,*decoder_string,&h);
if (VALID_HANDLE(h)) {
if (ntries>0) {
sprintf(s+1,"%d %c",(int) ((100.*nright)/((double) ntries)),'%');
s[0] = strlen(s+1);
}
else {
sprintf(s+1," ");
s[0] = strlen(s+1);
}
TextSize((short) 18);
TextFont(geneva);
SetIText(h,(unsigned char *) s);
normal_text_style();
}
}
if (they_gave_answer || cleared_scores || need_to_redraw_all) {
Handle hh;
Rect rr;
hh = GetIcon((short) current_icon);
if (VALID_HANDLE(hh)) {
get_rect_by_nifty_label(my_complete_window->the_window,
"ic",1,*decoder_string, &rr);
PlotIcon(&rr,hh);
}
}
if (they_gave_answer && !right) {
int i,this_chord_item,nifty_index;
char nifty_name[30];
//-- old method of showing correct answer was to erase other buttons;
//-- now we have to redraw them:
if (need_to_redraw_erased_buttons) {
rest((int)( 2. * 2000.*60./typical_tempo));
for (i=0; i<=MAXPART; i++) { //-- normally we break out of inside of loop
part_number_to_nifty_label(nifty_name,&nifty_index,*decoder_string,i);
if (nifty_index < 0) break;
this_chord_item = part_num_to_chord_item(i,*decoder_string);
if (this_chord_item!=0 && !IS_CHECK_BOX(this_chord_item)
&& !is_in_list(item,n_items,this_chord_item)) {
show_ctl_by_nifty_label(my_complete_window->the_window,
nifty_name,nifty_index,*decoder_string);
}
}
}
}
if (!ready_to_update) {
SetPort(save_graf);
}
}//---end if need to redraw
if (!they_have_chord_to_consider) {
chose_new_chord = 1;
choose_chord(item,&n_items,difficulty,enabled_list,n_enabled,
min_notes,max_notes);
make_chord(chord,&n_notes,item,n_items);
root = 0; //-- this gets changed below when we shift the
// whole chord to its final pitch, and
// also in case we reinterpret the chord
fiddle_with_chord_voicing(chord,n_notes,inversions_allowed);
//-- kludge: if it's a m7 with 3 in bass, reinterpret as a 6 chord
if (n_notes==4 && notes_to_quick_label(chord,n_notes)==2131) {
if (make_0_to_11(chord[0]-root)==3) {
n_items = 2;
item[0] = CHORD_MAJ_TRIAD;
item[1] = CHORD_6;
make_chord(chord,&n_notes,item,n_items);
root = 0;
}
}
center = mean_note+(random_double()-.5)*6+(random_double()-.5)*6;
avg = avg_chord(chord,n_notes);
for (i=0; i<n_notes; i++) {
chord[i] += center-avg;
}
root += center-avg;
duration = choose_duration(typical_tempo);
if (random_double()<.5) {
arpeggio_direction = 1;
}
else {
arpeggio_direction = -1;
}
play_it = 1;
they_have_chord_to_consider = 1;
n_guess = 0;
guessed_basic_part = 0;
}
if (play_it || play_previous) {
if (play_bass) {
play_chord(1,my_snd_chan,chord,duration,1);
}
else {
int chord_to_play[MAX_CHORD_NOTES],play_n_notes;
if (play_it) {
copy_chord(chord_to_play,chord,n_notes);
play_n_notes = n_notes;
}
else { //-- play previous chord
copy_chord(chord_to_play,previous_chord,previous_n_notes);
play_n_notes = previous_n_notes;
}
if (chord_or_arpeggio==1 && !play_arpeggio) {
play_chord(play_n_notes,my_snd_chan,chord_to_play,duration,1);
}
else {
int i,x[4],d,start,stop;
if (chord_or_arpeggio==1) //--we're really set to play chords, so speed
// up for arpeggio
d = duration*((double) TYPICAL_TEMPO_CHORD )
/((double) TYPICAL_TEMPO_ARPEGGIO);
else
d = duration;
if (arpeggio_direction>0) {
start = 0;
stop = play_n_notes-1;
}
else {
start = play_n_notes-1;
stop = 0;
}
i = start;
for (;;) {
x[0] = chord_to_play[i];
play_chord(1,my_snd_chan,x,d,1);
if (i==stop) break;
i += arpeggio_direction;
}
}
}
}
//--- A second block of code for updating the window: the chord symbol
// for their guess. The reason it has to be down here is that
// if we're moving on to the next chord, we wait until now to
// erase their previous guess, _after_ playing the new chord
if (chose_new_chord || changed_guess || need_to_redraw_all) {
Handle h;
char s[50];
int i;
if (!ready_to_update) {
GetPort(&save_graf);
SetPort(my_complete_window->the_window);
}
get_item_by_nifty_label(my_complete_window->the_window,
"guess",1,*decoder_string,&h);
if (VALID_HANDLE(h)) {
describe_chord(s+1,n_guess,guess);
s[0] = strlen(s+1);
TextSize((short) 18);
TextFont(geneva);
SetIText(h,(unsigned char *) s);
normal_text_style();
}
if (!ready_to_update) {
SetPort(save_graf);
}
}
}
double
scale_difficulty(int raw)
{
double z,y;
z = raw;
if (z>20.) z=20.;
y = .3*z*z+z-1.;
if (z>=10.) y += 1000.*((1./(21.-z))-(1./11.));
return y;
}
unsigned int
plain_chord_item(unsigned int x)
{
unsigned int y;
y = 0x7FFFL;
return x & y;
}
int
part_num_to_chord_item(int part,char *decoder_string)
{
char nifty_name[50];
int nifty_index;
part_number_to_nifty_label(nifty_name,&nifty_index,decoder_string,part);
if (strcmp(nifty_name,"majt")==0) return CHORD_MAJ_TRIAD;
if (strcmp(nifty_name,"mint")==0) return CHORD_MIN_TRIAD;
if (strcmp(nifty_name,"dimt")==0) return CHORD_DIM_TRIAD;
if (strcmp(nifty_name,"augt")==0) return CHORD_AUG_TRIAD;
if (strcmp(nifty_name,"sust")==0) return CHORD_SUS_TRIAD;
if (strcmp(nifty_name,"b5t")==0) return CHORD_FLAT5_TRIAD;
if (strcmp(nifty_name,"7")==0) return CHORD_DOM7;
if (strcmp(nifty_name,"m7")==0) return CHORD_MIN7;
if (strcmp(nifty_name,"maj7")==0) return CHORD_MAJ7;
if (strcmp(nifty_name,"o7")==0) return CHORD_DIM7;
if (strcmp(nifty_name,"h")==0) return CHORD_HALF_DIM;
if (strcmp(nifty_name,"b9")==0) return CHORD_FLAT9;
if (strcmp(nifty_name,"9")==0) return CHORD_9;
if (strcmp(nifty_name,"#9")==0) return CHORD_SHARP9;
if (strcmp(nifty_name,"11")==0) return CHORD_11;
if (strcmp(nifty_name,"#11")==0) return CHORD_SHARP11;
if (strcmp(nifty_name,"b6")==0) return CHORD_FLAT6;
if (strcmp(nifty_name,"6")==0) return CHORD_6;
if (strcmp(nifty_name,"a7")==0) return CHORD_ADD7;
if (strcmp(nifty_name,"amaj7")==0) return CHORD_ADDMAJ7;
if (strcmp(nifty_name,"no1")==0) return CHORD_NO1;
if (strcmp(nifty_name,"no3")==0) return CHORD_NO3;
if (strcmp(nifty_name,"no5")==0) return CHORD_NO5;
if (strcmp(nifty_name,"cmajt")==0) return CHORD_MAJ_TRIAD | 0x8000;
if (strcmp(nifty_name,"cmint")==0) return CHORD_MIN_TRIAD | 0x8000;
if (strcmp(nifty_name,"cdimt")==0) return CHORD_DIM_TRIAD | 0x8000;
if (strcmp(nifty_name,"caugt")==0) return CHORD_AUG_TRIAD | 0x8000;
if (strcmp(nifty_name,"csust")==0) return CHORD_SUS_TRIAD | 0x8000;
if (strcmp(nifty_name,"cb5t")==0) return CHORD_FLAT5_TRIAD | 0x8000;
if (strcmp(nifty_name,"c7")==0) return CHORD_DOM7 | 0x8000;
if (strcmp(nifty_name,"cm7")==0) return CHORD_MIN7 | 0x8000;
if (strcmp(nifty_name,"cmaj7")==0) return CHORD_MAJ7 | 0x8000;
if (strcmp(nifty_name,"co7")==0) return CHORD_DIM7 | 0x8000;
if (strcmp(nifty_name,"ch")==0) return CHORD_HALF_DIM | 0x8000;
if (strcmp(nifty_name,"cb9")==0) return CHORD_FLAT9 | 0x8000;
if (strcmp(nifty_name,"c9")==0) return CHORD_9 | 0x8000;
if (strcmp(nifty_name,"c#9")==0) return CHORD_SHARP9 | 0x8000;
if (strcmp(nifty_name,"c11")==0) return CHORD_11 | 0x8000;
if (strcmp(nifty_name,"c#11")==0) return CHORD_SHARP11 | 0x8000;
if (strcmp(nifty_name,"cb6")==0) return CHORD_FLAT6 | 0x8000;
if (strcmp(nifty_name,"c6")==0) return CHORD_6 | 0x8000;
if (strcmp(nifty_name,"ca7")==0) return CHORD_ADD7 | 0x8000;
if (strcmp(nifty_name,"camaj7")==0) return CHORD_ADDMAJ7 | 0x8000;
if (strcmp(nifty_name,"cno1")==0) return CHORD_NO1 | 0x8000;
if (strcmp(nifty_name,"cno3")==0) return CHORD_NO3 | 0x8000;
if (strcmp(nifty_name,"cno5")==0) return CHORD_NO5 | 0x8000;
return 0;
}
//
// Play with chord voicing.
// If inversions_allowed is false, leave root in bass. Assume chord[0] is root.
// Chord is always returned as a sorted list, lowest note first.
//
void
fiddle_with_chord_voicing(int *chord,int n,int inversions_allowed)
{
int i,lowest,height_above_bass,height_above_lower_neighbor,
height_below_upper_neighbor,nearest_neighbor,z,
changed_voicing,j,min_height;
int used_up[MAX_CHORD_NOTES];
if (inversions_allowed)
lowest = 0;
else
lowest = 1;
do {
for (j=0; j<n; j++) {
used_up[j] = 0;
}
for (j=lowest; j<n; j++) {
do {
i = -1 + lowest + random_integer(n-lowest);
} while(used_up[i]);
used_up[i] = 1;
changed_voicing = 0;
if (i>0) {
height_above_lower_neighbor = chord[i]-chord[i-1];
height_above_bass = chord[i]-chord[0];
}
else {
height_above_lower_neighbor = 0;
height_above_bass = 999;
}
if (i<n-1)
height_below_upper_neighbor = chord[i+1]-chord[i];
else
height_below_upper_neighbor = 999;
if (height_below_upper_neighbor<height_above_lower_neighbor)
nearest_neighbor=height_below_upper_neighbor;
else
nearest_neighbor=height_above_lower_neighbor;
if (12.+random_double()*6.<height_above_lower_neighbor) {
chord[i] -= 12;
changed_voicing = 1;
}
else {
if (nearest_neighbor<3 && random_double()*3.>nearest_neighbor
&& random_double()<.5) {
chord[i] += 12;
changed_voicing = 1;
}
else {
switch(random_integer(3)) {
case 1: min_height = 0; break;
case 2: min_height = 3; break;
case 3: min_height = 5; break;
}
if (height_above_bass<=min_height) {
chord[i] += 12;
changed_voicing = 1;
}
}
}
if (changed_voicing)
sort_int_list(chord,n);
}
} while(random_double()<.3);
}
// large difficulty (say 999) means all chords equally likely
// difficulty of 7 means strangest chords 7 times less likely than most normal
// Note that what we are passed is the _scaled_ difficulty.
void
choose_chord(int *item,int *n,double difficulty,int *enabled_list,int n_enabled,
int min_notes,int max_notes)
{
int z,what,chord[MAX_CHORD_NOTES],new_chord[MAX_CHORD_NOTES],
n_notes,diss,new_n_notes,seal_of_approval,
save_items[MAX_CHORD_NOTES],save_n_items,bogus,i,
qqq;
do { //--- outer loop checks if simplification gives something they disabled
do { //--- until we find a chord that's not too dissonant
//---- first, choose the basic chord quality
do {
do {
z = random_double()*13.;
} while (z<1 || z>11);
switch(z) {
case 1: what = CHORD_MAJ_TRIAD; break;
case 2: what = CHORD_MIN_TRIAD; break;
case 3: what = CHORD_DIM_TRIAD; break;
case 4: what = CHORD_AUG_TRIAD; break;
case 5: what = CHORD_SUS_TRIAD; break;
case 6: what = CHORD_FLAT5_TRIAD; break;
case 7: what = CHORD_DOM7; break;
case 8: what = CHORD_MIN7; break;
case 9: what = CHORD_MAJ7; break;
case 10: what = CHORD_DIM7; break;
case 11: what = CHORD_HALF_DIM; break;
}
}while (
(random_double()*difficulty+1.<how_unusual_is_basic_chord(what)
&& random_double()<.8)
|| !is_in_list(enabled_list,n_enabled,what) );
item[0] = what;
*n = 1;
make_chord(chord,&n_notes,item,*n);
//---- now, choose some added tones; use plenty, because it
// can always get rejected later for being too dissonant
while (random_double()<.9 && random_double()*(.3*difficulty+1.2)>1.
&& n_notes<=max_notes+3) {
do {
do {
z = random_double()*14.;
} while (z<1 || z>12);
switch(z) {
case 1: what = CHORD_FLAT9; break;
case 2: what = CHORD_9; break;
case 3: what = CHORD_SHARP9; break;
case 4: what = CHORD_11; break;
case 5: what = CHORD_SHARP11; break;
case 6: what = CHORD_FLAT6; break;
case 7: what = CHORD_6; break;
case 8: what = CHORD_ADD7; break;
case 9: what = CHORD_ADDMAJ7; break;
case 10: what = CHORD_NO1; break;
case 11: what = CHORD_NO3; break;
case 12: what = CHORD_NO5; break;
}//--end switch
make_chord(chord,&n_notes,item,*n);
new_n_notes = n_notes;
if (!is_in_list(item,*n,what)) {
add_to_list(item,n,what);
make_chord(new_chord,&new_n_notes,item,*n);
remove_from_list(item,n,what);
}
diss = dissonance(new_chord,new_n_notes);
seal_of_approval =
!(
random_double()*difficulty<how_unusual_is_added_tone(what)
|| !is_in_list(enabled_list,n_enabled,what)
|| new_n_notes==n_notes
);
} while (!seal_of_approval
&& random_double()>.05 //--make sure it doesn't loop forever
);
//--while loop for picking one that's not too unusual
if (seal_of_approval) {
add_to_list(item,n,what);
make_chord(chord,&n_notes,item,*n);
}
else
break;
}//--while loop for adding one tone after another
//-- Special cases for combinations of items that don't really
// make sense:
bogus = (*n==2 && item[1]==CHORD_NO1)
//... without root, becomes a triad
|| (*n==2 && item[0]==CHORD_DIM7 && item[1]>=LO_OMITTED_TONE
&& item[1]<=HI_OMITTED_TONE)
//...=dim triad
|| (*n==2 && item[0]==CHORD_HALF_DIM && item[1]==CHORD_NO3)
//... =b5 triad
;
make_chord(chord,&n_notes,item,*n);
diss = dissonance(chord,n_notes);
qqq = random_double()*difficulty;
while (random_double()<.2) qqq += 1.; //-- avoid infinite loop on low
// difficulty
} while(qqq<2.*diss-1.
|| bogus || n_notes<min_notes || n_notes>max_notes);
//--end outer while loop; if too dissonant, try again
//
// Now simplify the chord. But if simplification results in a
// reduction of number of items by one or less, and the simplified
// version uses disabled items, go back to the more complicated
// version.
//
copy_chord(save_items,item,*n);
save_n_items = *n;
simplify_chord(item,n);
bogus = 0;
for (i=0; i<*n; i++) {
bogus = bogus || !is_in_list(enabled_list,n_enabled,item[i]);
}
if (bogus && *n>=save_n_items-1) {
bogus = 0;
copy_chord(item,save_items,save_n_items);
*n = save_n_items;
}
} while (bogus);
}
void
make_about_chord_window()
{
int previous_n_windows;
if (!about_chord_window_exists) {
previous_n_windows = n_windows;
make_window(ABOUT_CHORD_DLOG_ID,"\p","\p","\p","\p",
&about_chord_den_mother);
if (n_windows==previous_n_windows+1) {
where_is_about_chord_complete_window = my_windows[n_windows-1];
about_chord_window_exists = 1;
}
else {
where_is_about_chord_complete_window = (complete_window *) 0;
about_chord_window_exists = 0;
}
}
}